home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / freeWAIS-sf-1.1 / ir / irtfiles.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-13  |  57.7 KB  |  1,950 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4.  
  5.    Brewster@think.com
  6. */
  7.  
  8. /* Copyright (c) CNIDR (see ../COPYRIGHT) */
  9.  
  10.  
  11. /* Change log:
  12.  * $Log: irtfiles.c,v $
  13.  * Revision 1.29  1994/12/13  17:03:58  pfeifer
  14.  * *** empty log message ***
  15.  *
  16.  * Revision 1.26  1994/09/07  13:29:22  pfeifer
  17.  * ctype is now included from cdialect.h after inclusion of string.h.
  18.  * Since ctype.h (the local version defines strcmp and friends, inclusion o
  19.  * of string.h after that caused probems
  20.  *
  21.  * Revision 1.25  1994/08/22  14:11:52  pfeifer
  22.  * waisindex: files on the command line my now contaion extensions ".gz"
  23.  *
  24.  * Revision 1.24  1994/08/08  10:29:53  pfeifer
  25.  * Fixed the MAX_OCCURANCES - STOP_WORD_FLAG bug
  26.  *
  27.  * Revision 1.23  1994/08/05  08:53:59  pfeifer
  28.  * Fix for total_word_count bug
  29.  *
  30.  * Revision 1.22  1994/08/05  07:12:22  pfeifer
  31.  * Release beta 04
  32.  *
  33.  * Revision 1.20  1994/07/13  09:55:23  pfeifer
  34.  * Negative numerics.
  35.  *
  36.  * Revision 1.19  1994/07/13  07:53:26  pfeifer
  37.  * beta 02
  38.  *
  39.  * Revision 1.18  1994/06/09  16:49:50  pfeifer
  40.  * changed index_text_file to put the URL in the filename_id. So headline
  41.  * is freely customizable.
  42.  *
  43.  * Revision 1.17  1994/05/27  11:06:20  huynh1
  44.  * updated index_text_file, field_index_text_file, index_directory. beta
  45.  *
  46.  * Revision 1.16  1994/05/21  14:29:38  pfeifer
  47.  * fixes: add_word and field_add_word are called with missing
  48.  *        word_position parameter
  49.  *
  50.  * Revision 1.15  1994/05/20  12:52:24  huynh1
  51.  * weights
  52.  *
  53.  * Revision 1.13  1994/03/23  13:09:35  pfeifer
  54.  * removed the include iso.h
  55.  *
  56.  * Revision 1.12  1994/03/17  13:52:18  pfeifer
  57.  * patchlevel 06
  58.  *
  59.  * Revision 1.11  1994/03/17  13:28:19  huynh1
  60.  * index headlines.
  61.  * Patchlevel 06.
  62.  *
  63.  * Revision 1.10  1994/03/16  16:48:47  pfeifer
  64.  * fixed bug in cleanHeadline
  65.  *
  66.  * Revision 1.9  1994/03/11  15:16:15  huynh1
  67.  * not index headlines.
  68.  * Patchlevel 05.
  69.  *
  70.  * Revision 1.8  1994/03/10  17:33:56  huynh1
  71.  * Patchlevel 05
  72.  *
  73.  * Revision 1.7  1994/03/08  20:45:07  huynh1
  74.  * Patchlevel 04
  75.  *
  76.  * Revision 1.6  1994/02/14  10:33:48  huynh1
  77.  * new code for field concept added.
  78.  *
  79.  * Revision 1.4  1993/09/22  16:07:52  pfeifer
  80.  * Fixed word breaking for german ISO Umlaute and sz
  81.  *
  82.  * Revision 1.3  1993/06/04  10:23:15  pfeifer
  83.  * Pachtlevel BIBDB
  84.  *
  85.  * Revision 1.2  1993/06/01  14:05:54  pfeifer
  86.  * Added code for soundex/phonix indexing and retrieval
  87.  *
  88.  * Revision 1.1  1993/02/16  15:05:35  freewais
  89.  * Initial revision
  90.  *
  91.  * Revision 1.32  92/05/06  17:32:14  jonathan
  92.  * Added new global for current_filename and current_filecount (from
  93.  * riddle@rice.edu).
  94.  * 
  95.  * Revision 1.31  92/04/30  12:25:09  jonathan
  96.  * changed a couple of s_free's to free's for ULTRIX CC.
  97.  * 
  98.  * Revision 1.30  92/04/29  08:09:55  shen
  99.  * add global variable "_indexable_section", default is true
  100.  * 
  101.  * Revision 1.29  92/04/28  17:53:24  jonathan
  102.  * Replaced directory routines with scandir.
  103.  * 
  104.  * Revision 1.28  92/03/20  11:02:55  jonathan
  105.  * Added code to handle switches for word_pairs and word_postition info.
  106.  * 
  107.  * Revision 1.27  92/02/13  11:23:21  jonathan
  108.  * Removed printable_time() from index logging, since it's done by waislog.
  109.  * 
  110.  * Revision 1.26  92/02/12  13:31:29  jonathan
  111.  * Added "$Log" so RCS will put the log message in the header
  112.  * 
  113. */
  114.  
  115. /* 
  116.  * Indexes the words in a text file.
  117.  * 
  118.  * Port of irtfiles.lisp.
  119.  *
  120.  * -brewster 6/90
  121.  */
  122.  
  123. /* the main functions are:
  124.  *   index_text_file
  125.  *   index_directory
  126.  *
  127.  * Some of the policy issues coded in this file are
  128.  *   What extra weight should the headline get?
  129.  *
  130.  */
  131.  
  132. #define EXTERN_WORD_DELIMITER
  133. #define EXTERN_MAKE_COMPILER_HAPPY
  134. /* #include <ctype.h> */
  135. /* #include <string.h> */
  136. #include "panic.h"  
  137. #include "irdirent.h"
  138. #include "irhash.h"
  139. #include "cutil.h"
  140. #include "futil.h"
  141. #include "irfiles.h"
  142. #include "irtfiles.h"
  143.  
  144. #include "ircfiles.h"   /* dgg, need for genbank_header_function test */
  145.  
  146. #ifdef STEM_WORDS
  147. #include "stemmer.h"   
  148. #endif
  149.  
  150. #ifdef FIELDS /* tung, 1/94 */
  151. #include "field_index.h"
  152. #endif
  153.  
  154. #ifdef NEW_WEIGHT /* tung, 5/94 */
  155. #include "weight.h"
  156. #endif
  157.  
  158. #ifdef SOUND
  159. #include "soundex.h"
  160. #endif
  161.  
  162. #ifndef THINK_C
  163. #include <sys/types.h>
  164. #include <sys/stat.h>
  165. #endif /* ndef THINK_C */
  166.  
  167. #define MAX_LINE_LENGTH 1000 /* characters */
  168. #define extra_weight_for_header 10
  169.  
  170. #ifdef UNIX
  171. #define PRINT_AS_INDEXING true /* also defined in irfiles.c */
  172. #else 
  173. #define PRINT_AS_INDEXING false
  174. #endif
  175.  
  176. char* header_flag_1;
  177. char* header_flag_2;
  178. long len_of_files_since_last_delete = 0;
  179. long len_of_files_since_last_flush = 0;
  180. long total_indexed_file_length = 0;
  181.  
  182. boolean indexingForBeta = false;
  183.  
  184. long _indexable_section = 1;
  185.  
  186. char *current_filename = NULL;
  187. int  current_filecount = 0;
  188.  
  189. boolean index_contents = true;
  190.  
  191.  
  192. #define keyword_weight 1
  193.  
  194. /* keywords from command line (set in irbuild.c), used in finish_document */
  195. char* keywords = NULL;
  196.  
  197. /* name of keyword file from command line, used in finish_document */
  198. char* keyword_filename = NULL;
  199.  
  200.  
  201.  
  202. /*  Handling Word Pairs */
  203.  
  204. /* makes a word_pair out of a two words:
  205. make_joint_word("abcdefghijklmnopqrstuvwxyz", "123456789012345678901");
  206.   "abcdefghij1234567890"
  207. make_joint_word("abcdefghijkl", "123");
  208.   "abcdefghij123"
  209. make_joint_word("abc", "123");
  210.   "abc123" */
  211.  
  212. char *make_joint_word(word1, word2)
  213.      char* word1;
  214.      char* word2;
  215. {
  216.   static char new_word[MAX_WORD_LENGTH + 1];
  217.   bzero(new_word, MAX_WORD_LENGTH + 1);
  218.   strncpy(new_word, word1, MAX_WORD_LENGTH / 2);
  219.   strncpy(new_word + MINIMUM(MAX_WORD_LENGTH / 2, strlen(word1)),
  220.       word2, MAX_WORD_LENGTH - (MAX_WORD_LENGTH / 2));
  221.   return(new_word);    
  222. }
  223.  
  224. /* returns 0 is successful, non-0 if error */
  225. static long add_word_before_pairs _AP((char *word, long char_pos,
  226.                        long line_pos, long weight,
  227.                        long doc_id, time_t date,
  228.                        boolean capitalized, database* db,
  229.                        boolean word_position, boolean word_pairs));
  230.  
  231. static long
  232.   add_word_before_pairs(word, char_pos, line_pos,
  233.             weight, doc_id, date, capitalized, db,
  234.             word_position, word_pairs)
  235. char *word;    /* the word to be indexed, this could be a
  236.            word pair. If NULL there are no more words
  237.            to be indexed */
  238. long char_pos;    /* the position of the start of the
  239.            word */
  240. long line_pos;    /* this is passed for the best
  241.            section calculation */
  242. long weight;    /* how important the word looks
  243.            syntactically (such as is it bold)
  244.            NOT used by signature system */
  245. long doc_id;     /* current document, this will never be 0 */
  246. time_t date; /* display day of this document, 0 if not known */
  247. boolean capitalized; /* if the word started with a cap */
  248. database* db; /* database to insert the document */
  249. boolean word_position; /* if true, include word position in index. */
  250. boolean word_pairs; /* if true, add pairs of capitalized words */
  251. {
  252.   static char last_word[MAX_WORD_LENGTH + 1];
  253.   static long last_doc_id = -1;
  254.  
  255.   /* The way it works is it remembers if the last word if it was
  256.      capitalized (if not it clears the saved word).  
  257.      If another capitalized word comes along next
  258.      (and it is in the same document), then it makes a joint word and calls 
  259.      add_word with it. 
  260.      
  261.      This does not throw away stopwords before forming pairs, so it will 
  262.      not be quite what CMDRS does.  This should only be used in seeker 
  263.      and serial searching before proximity is used.
  264.      
  265.      */
  266.  
  267.   if(capitalized && word_pairs){
  268.     if(last_word[0] != '\0' && last_doc_id == doc_id){
  269.       add_word(make_joint_word(last_word, word), /* added word_position (up) */
  270.                char_pos, line_pos, weight, doc_id, date, 1L, db, word_position);
  271.     }
  272.     else{
  273.       last_word[0] = '\0';
  274.     }
  275.     strncpy(last_word, word, MAX_WORD_LENGTH);
  276.     last_doc_id = doc_id;
  277.   }
  278.   else{                /* not capitalized or word_pairs is false */
  279.     last_word[0] = '\0';
  280.   }
  281.  
  282.   return(add_word(word, char_pos, line_pos, weight, doc_id, date, 0L, db, word_position));
  283. }
  284.  
  285. #ifdef FIELDS /* tung, 1/94 */
  286. static long field_add_word_before_pairs _AP((char *word, long char_pos,
  287.                                              long line_pos, long weight,
  288.                                              long doc_id, time_t date,
  289.                                              boolean capitalized, database* db,
  290.                                              boolean word_position, boolean word_pairs));
  291.      
  292. static long
  293.   field_add_word_before_pairs(word, char_pos, line_pos,
  294.                               weight, doc_id, date, capitalized, db,
  295.                               word_position, word_pairs)
  296. char *word;    /* the word to be indexed, this could be a
  297.            word pair. If NULL there are no more words
  298.            to be indexed */
  299. long char_pos;    /* the position of the start of the
  300.            word */
  301. long line_pos;    /* this is passed for the best
  302.            section calculation */
  303. long weight;    /* how important the word looks
  304.            syntactically (such as is it bold)
  305.            NOT used by signature system */
  306. long doc_id;     /* current document, this will never be 0 */
  307. time_t date; /* display day of this document, 0 if not known */
  308. boolean capitalized; /* if the word started with a cap */
  309. database* db; /* database to insert the document */
  310. boolean word_position; /* if true, include word position in index. */
  311. boolean word_pairs; /* if true, add pairs of capitalized words */
  312. {
  313.   static char field_last_word[MAX_WORD_LENGTH + 1];
  314.   static long field_last_doc_id = -1;
  315.   static last_current_field_id = -1;
  316.  
  317.   /* The way it works is it remembers if the last word if it was
  318.      capitalized (if not it clears the saved word).  
  319.      If another capitalized word comes along next
  320.      (and it is in the same document), then it makes a joint word and calls 
  321.      add_word with it. 
  322.      
  323.      This does not throw away stopwords before forming pairs, so it will 
  324.      not be quite what CMDRS does.  This should only be used in seeker 
  325.      and serial searching before proximity is used.
  326.      
  327.      */
  328.   if(last_current_field_id != db->current_field_id) {
  329.     last_current_field_id = db->current_field_id; /* defined in field.c */
  330.     field_last_word[0] = '\0';
  331.   }
  332.   if(capitalized && word_pairs){
  333.     if(field_last_word[0] != '\0' && field_last_doc_id == doc_id){
  334.       field_add_word(make_joint_word(field_last_word, word), 
  335.                      /* Hey Tung: If you don't prototype, you *must* check */
  336.                      /* the parameters. You forgott word_position!  (up) */
  337.                char_pos, line_pos, weight, doc_id, date, 1L, db, word_position);
  338.     }
  339.     else field_last_word[0] = '\0';
  340.     strncpy(field_last_word, word, MAX_WORD_LENGTH);
  341.     field_last_doc_id = doc_id;
  342.   }
  343.   else field_last_word[0] = '\0';
  344.   
  345.   return(field_add_word(word, char_pos, line_pos, weight, doc_id, date, 0L, db, word_position));
  346. }
  347. #endif
  348.  
  349.  
  350.  
  351. #ifdef NOTUSED
  352. #define WORD_LETTERS  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
  353.  
  354.  
  355. static char *new_word _AP((char* line,char* word));
  356.  
  357. static char *new_word(line,word)
  358. char *line;
  359. char *word;
  360. {
  361.   /* This copies the first word from line into word while downcasing it.
  362.      It returns a pointer into line that is after the word,
  363.      which can be used to call this function again.
  364.      If there are no words left, then NULL is returned,
  365.      and word is length 0.
  366.      There has got to be a better way.
  367.      */
  368.   long i = 0;
  369.   char *beginning_ptr = strpbrk(line, WORD_LETTERS);
  370.   char *next_word;
  371.   long length;
  372.   if(NULL == beginning_ptr){
  373.     word[0] = '\0';
  374.     return(NULL);
  375.   }
  376.   length  = strspn(beginning_ptr, WORD_LETTERS);
  377.   next_word = length + beginning_ptr;
  378.  
  379.   length = MINIMUM(MAX_WORD_LENGTH,length);
  380.   for(i=0; i<length; i++){
  381.     word[i] = char_downcase((unsigned long)*beginning_ptr++);
  382.   }
  383.   word[i] = '\0';
  384.   return(next_word);
  385. }
  386.  
  387. static boolean reasonable_word _AP((char* word));
  388.  
  389. static boolean reasonable_word(word)
  390. char* word;
  391. /* this should be more sophisticated */
  392. {
  393.   if(strlen(word) > 1){
  394.     return(TRUE);
  395.   }
  396.   else{
  397.     return(FALSE);
  398.   }
  399. }
  400.  
  401. #endif /* def NOTUSED */
  402.  
  403.  
  404.  
  405. /* MAPPING A FUNCTION OVER WORDS (QUICKLY) */
  406.  
  407.  
  408. /* map_over_words("foo bar baz", 0L, 1L, 0L, &integer, false, db, dummy_wordfunction) */
  409. static long dummy_wordfunction(word, char_pos, line_pos,
  410.                    weight, doc_id, date, capitalized, db)
  411.      char *word;    /* the word to be indexed, this could be a
  412.                word pair. If NULL there are no more words
  413.                to be indexed */
  414.      long char_pos;    /* the position of the start of the
  415.                word */
  416.      long line_pos;    /* this is passed for the best
  417.                section calculation */
  418.      long weight;    /* how important the word looks
  419.                syntactically (such as is it bold)
  420.                NOT used by signature system */
  421.      long doc_id;     /* current document, this will never be 0 */
  422.      time_t date; /* display day of this document, 0 if not known */
  423.      boolean capitalized; /* if the word started with a cap */
  424.      database* db; /* database to insert the document */
  425. {
  426.   if(word != NULL)
  427.     printf("word: %s, char_pos: %ld\n", word, char_pos);
  428.   return(0);
  429. }
  430.  
  431. #ifdef FIELDS /* tung, 4/94 */
  432. #ifdef STEM_WORDS
  433. extern boolean index_stemming; /* defined in field_index.c */
  434. #endif
  435. #endif
  436.  
  437. /* returns the number of words added, or -1 if an error occurred */
  438. long map_over_words(line,
  439.             document_id,
  440.             weight,
  441.             file_position_before_line,
  442.             line_length,
  443.             newline_terminated,
  444.             db,
  445.             wordfunction,
  446.             word_position, word_pairs,
  447. #ifdef SOUND
  448.             minwordlen, type)    
  449. #else
  450.             minwordlen)    /* dgg */
  451. #endif
  452.      char* line;
  453.      long document_id;
  454.      long weight;
  455.      long file_position_before_line;
  456.      long *line_length;
  457.      boolean *newline_terminated;
  458.      database* db;
  459.      wordfunc *wordfunction;
  460.      boolean word_position, word_pairs;
  461.      int    minwordlen;
  462. #ifdef SOUND
  463.      char* type;
  464. #endif
  465. {
  466.   /* Add words to the index if it should be done. 
  467.    * Returns the number of words added.
  468.    * Should it return the amount of weight added?
  469.    * The line length is side effected with the length of the line.
  470.    * Newline_terminated is set based on whether the last character
  471.    * in the string was a newline.  If it was not, then it fgets probably
  472.    * did not retrieve the whole line.
  473.    */
  474.  
  475.   long position_in_word = 0;
  476.   long word_count = 0;
  477.   unsigned long ch;
  478.   long char_count = 0;
  479.   boolean capitalized = false; /* if the word starts with a cap */
  480.   char word[MAX_WORD_LENGTH + 1];
  481.   
  482.   
  483.   for(ch = (unsigned char)line[char_count++]; 
  484.       ch != '\0'; ch = (unsigned char)line[char_count++]){
  485. #ifdef BIO
  486.     boolean alnum = (wordDelimiter(ch) == NOT_DELIMITER);
  487. #else
  488.     boolean alnum = isalnum(ch);
  489. #endif
  490.  
  491. #ifdef FIELDS /* tung, 1/94 */
  492.     if(db->number_of_fields > 0) {
  493.       if(db->fields[db->current_field_position].numeric) {
  494.     minwordlen = 1;
  495.         if(ch == ',') {
  496.           ch = '.' ;
  497.           alnum = true;
  498.         } else if (ch == '-') { /* negative number */
  499.           /* printf("map_over_words: found negative number\n"); */
  500.           /* ch='a'; */
  501.       alnum = true;
  502.         } else if (ch == '.') { 
  503.           /* printf("map_over_words: found '.'\n"); */
  504.       alnum = true;
  505.     }
  506.       }
  507.     }
  508. #endif    
  509.     
  510.  
  511.     if(alnum){
  512.       /* put the character in the word if not too long */
  513.       if(position_in_word == 0)
  514.     capitalized = isupper((unsigned long)ch)?true:false;
  515.       if(position_in_word < MAX_WORD_LENGTH){
  516.     word[position_in_word++] = char_downcase((unsigned long)ch);
  517.       }
  518.     }
  519.     else{ /* not an in a word */
  520.       if(position_in_word != 0){
  521.     /* then we have collected a word */
  522.     if(position_in_word >= minwordlen){ /* is it reasonable ? */
  523.       word[position_in_word] = '\0';
  524.           
  525.           /* call the stemmer */
  526. #ifdef FIELDS /* tung, 4/94 */
  527.       if(db->number_of_fields > 0) {
  528. #ifdef STEM_WORDS
  529.         if(index_stemming) /* defined in field_index.c */
  530.           stemmer(word);
  531.       }
  532.       else {
  533.         if(db->stemming) 
  534.           stemmer(word);
  535.       }
  536. #endif
  537. #else
  538. #ifdef STEM_WORDS
  539.       stemmer(word);
  540. #endif
  541. #endif
  542.           
  543. #ifdef SOUND /* tung, 1/94 */
  544.           if(type == NULL) {
  545. #endif
  546.             if(0 !=
  547.                (*wordfunction)(word,
  548.                                file_position_before_line + char_count,  
  549.                                /*^^ dgg, this param is supposed to be start-of-word, but char_count is now at end-of-word !*/
  550.                                0L, /* line_pos */
  551.                                weight, 
  552.                                document_id, 
  553.                                (time_t)0L,
  554.                                capitalized,
  555.                                db,
  556.                                word_position,
  557.                                word_pairs))  
  558.               return(-1); /* error */
  559.             word_count++;
  560. #ifdef SOUND /* tung, 1/94 */
  561.           }
  562.           else {
  563. #endif
  564.             
  565. #ifdef SOUND
  566.             /*=========================== SOUNDEX / PHONIX ========================================*/
  567.             if (type != NULL) /* use only the first word (i.e. the surname) for SOUNDEX/PHONIX! */
  568.               if ((!strcmp(type, "SOUNDEX")) || (!strcmp(type, "PHONIX"))) 
  569.                 {
  570.                   char code[20];
  571.                   int  i;
  572.                   
  573.                   if (!strcmp(type, "SOUNDEX"))
  574.                     Soundex(word, code);
  575.                   else if (!strcmp(type, "PHONIX"))
  576.                     Phonix(word, code);
  577.                   
  578.                   code[0] = tolower(code[0]);
  579. #ifdef DEBUG
  580.                   fprintf(stderr, "%5d, %4s, %s\n", word_count, code, word); 
  581. #endif
  582.                   if (0 != (*wordfunction) (code,
  583.                                             file_position_before_line + char_count, 
  584.                                             0L, /* line_pos */
  585.                                             weight, 
  586.                                             document_id, 
  587.                                             (time_t) 0L,
  588.                                             capitalized,
  589.                                             db,
  590.                                             word_position,
  591.                                             word_pairs))
  592.                     return(-1);       /* error */
  593.                   
  594.                   word_count++;
  595.                 }
  596.             /*=====================================================================================*/
  597. #endif
  598. #ifdef SOUND /* tung, 1/94 */
  599.           } /* else { */
  600. #endif
  601.         }
  602.         position_in_word = 0;
  603.       }
  604.     }
  605.   }
  606.   /* finish last word */
  607.   if(position_in_word >= minwordlen){ /* is it reasonable ? */
  608.     word[position_in_word] = '\0';
  609.     
  610.     /* call the stemmer */
  611. #ifdef FIELDS /* tung, 4/94 */
  612.     if(db->number_of_fields > 0) {
  613. #ifdef STEM_WORDS
  614.       if(index_stemming) /* defined in field_index.c */
  615.     stemmer(word);
  616.     }
  617.     else {
  618.       if(db->stemming)
  619.     stemmer(word);
  620.     }
  621. #endif
  622. #else
  623. #ifdef STEM_WORDS
  624.     stemmer(word);
  625. #endif
  626. #endif
  627.     
  628. #ifdef SOUND /* tung, 1/94 */
  629.     if(type == NULL) {
  630. #endif
  631.       if(0 != (*wordfunction)(word,
  632.                               file_position_before_line + char_count, 
  633.                               0L,    /* line_pos */
  634.                               weight, 
  635.                               document_id, 
  636.                               (time_t)0L,
  637.                               capitalized,
  638.                               db,
  639.                               word_position, word_pairs))  
  640.         return(-1);
  641.       word_count++;
  642. #ifdef SOUND /* tung, 1/94 */
  643.     }
  644.     else {
  645. #ifdef SOUND
  646.       /*=========================== SOUNDEX / PHONIX ========================================*/
  647.       if (type != NULL) /* use only the word which contains alpha character for SOUNDEX/PHONIX! */
  648.         if ((!strcmp(type, "SOUNDEX")) || (!strcmp(type, "PHONIX"))) 
  649.           {
  650.             char code[20];
  651.             int  i;
  652.             
  653.             if (!strcmp(type, "SOUNDEX"))
  654.               Soundex(word, code);
  655.             else if (!strcmp(type, "PHONIX"))
  656.               Phonix(word, code);
  657.             
  658.             code[0] = tolower(code[0]);
  659. #ifdef DEBUG
  660.             fprintf(stderr, "%5d, %4s, %s\n", word_count, code, word); 
  661. #endif
  662.             if (0 != (*wordfunction) (code,
  663.                                       file_position_before_line + char_count, 
  664.                                       0L, /* line_pos */
  665.                                       weight, 
  666.                                       document_id, 
  667.                                       (time_t) 0L,
  668.                                       capitalized,
  669.                                       db,
  670.                                       word_position,
  671.                                       word_pairs))
  672.               return(-1);       /* error */
  673.             
  674.             word_count++;
  675.           }
  676.       /*=====================================================================================*/
  677. #endif
  678.     }
  679. #endif
  680.   } /* if(position_in_word >= minwordlen){ */
  681.   
  682. /* for debugging
  683.      if(char_count - 1 != strlen(line)) {
  684.      waislog(WLOG_HIGH, WLOG_ERROR, 
  685.      "char_count: %ld, strlen: %ld", char_count, strlen(line));
  686.      }
  687. */
  688.   if(newline_terminated != NULL){
  689.     if('\n' != line[char_count-2])
  690.       *newline_terminated = false;
  691.     else
  692.       *newline_terminated = true;
  693.   }
  694.   if(line_length != NULL)
  695.     *line_length = char_count - 1;
  696.   return(word_count);
  697. }
  698.  
  699.  
  700. static long add_words_if_appropriate 
  701.   _AP((char* line,long document_id,long weight,long file_position_before_line,
  702.        long* line_length,boolean* newline_terminated,database* db,
  703.        boolean word_position, boolean word_pairs,
  704.        int minwordlen));
  705.  
  706. static long 
  707. add_words_if_appropriate(line,
  708.              document_id,
  709.              weight,
  710.              file_position_before_line,
  711.              line_length,
  712.              newline_terminated,
  713.              db, 
  714.              word_position, word_pairs,
  715.              minwordlen)    /* dgg */
  716. char* line;
  717. long document_id;
  718. long weight;
  719. long file_position_before_line;
  720. long *line_length;
  721. boolean *newline_terminated;
  722. database* db;
  723. boolean word_position, word_pairs;
  724. int  minwordlen;
  725. {
  726.   /* Add words to the index if it should be done. 
  727.    * Returns the number of words added.
  728.    * Should it return the amount of weight added?
  729.    * The line length is side effected with the length of the line.
  730.    * Newline_terminated is set based on whether the last character
  731.    * in the string was a newline.  If it was not, then it fgets probably
  732.    * did not retrieve the whole line.
  733.    */
  734.  
  735.   long position_in_word = 0;
  736.   long word_count = 0;
  737.   char word[MAX_WORD_LENGTH + 1];
  738.   unsigned long ch;
  739.   long char_count = 0;
  740.   boolean capitalized = false; /* if the word starts with a cap */
  741.  
  742.   for(ch = (unsigned char)line[char_count++]; 
  743.       ch != '\0'; ch = (unsigned char)line[char_count++]){
  744. #ifdef BIO
  745. ,    boolean alnum = (wordDelimiter(ch) == NOT_DELIMITER);
  746. #else
  747.     boolean alnum = isalnum(ch);
  748. #endif
  749.  
  750. #ifdef FIELDS___
  751.     if(db->number_of_fields > 0) {
  752.       if(db->fields[db->current_field_position].numeric) {
  753.     minwordlen = 1;
  754.         if(ch == ',') {
  755.           ch = '.' ;
  756.           alnum = true;
  757.         } else if (ch == '-') { /* negative number */
  758.           /* printf("add_words_if_appropriate: found negative number\n"); */
  759.           /* ch='a'; */
  760.       alnum = true;
  761.         } else if (ch == '.') { 
  762.           /* printf("add_words_if_appropriate: found '.'\n"); */
  763.       alnum = true;
  764.     }
  765.       }
  766.     }
  767. #endif    
  768.     
  769.     if(alnum){
  770.       /* put the character in the word if not too long */
  771.       if(position_in_word == 0)
  772.     capitalized = isupper((unsigned long)ch)?true:false;
  773.       if(position_in_word < MAX_WORD_LENGTH){
  774.     word[position_in_word++] = char_downcase((unsigned long)ch);
  775.       }
  776.     }
  777.     else{ /* not an in a word */
  778.  
  779.       /* call the stemmer */
  780. #ifdef FIELDS /* tung, 4/94 */
  781.       if(db->number_of_fields > 0) {
  782. #ifdef STEM_WORDS
  783.     if(index_stemming)  /* defined in field_index.c */
  784.       stemmer(word);
  785.       }
  786.       else {
  787.     if(db->stemming)
  788.       stemmer(word);
  789.       }
  790. #endif
  791. #else
  792. #ifdef STEM_WORDS
  793.       stemmer(word);
  794. #endif
  795. #endif
  796.       
  797.       if(position_in_word != 0){
  798.     /* then we have collected a word */
  799.     if(position_in_word >= minwordlen){ /* is it reasonable ? */
  800.       word[position_in_word] = '\0';
  801.       add_word_before_pairs(word,
  802.                 file_position_before_line + char_count, 
  803.                 0L, /* line_pos */
  804.                 weight, 
  805.                 document_id, 
  806.                 (time_t)0L,
  807.                 capitalized,
  808.                 db,
  809.                 word_position, word_pairs);
  810.        word_count++;
  811.     }
  812.     position_in_word = 0;
  813.       }
  814.     }
  815.   }
  816.   /* finish last word */
  817.   if(position_in_word >= minwordlen){ /* is it reasonable ? */
  818.     word[position_in_word] = '\0';
  819.  
  820.     /* call the stemmer */
  821. #ifdef FIELDS /* tung, 4/94 */
  822.     if(db->number_of_fields > 0) {
  823. #ifdef STEM_WORDS
  824.       if(index_stemming) /* defined in field_index.c */
  825.     stemmer(word);
  826.     }
  827.     else {
  828.       if(db->stemming)
  829.     stemmer(word);
  830.     }
  831. #endif
  832. #else
  833. #ifdef STEM_WORDS
  834.     stemmer(word);
  835. #endif
  836. #endif
  837.     
  838.     add_word(word,
  839.          file_position_before_line + char_count, 
  840.          0L,        /* line_pos */
  841.          weight, 
  842.          document_id, 
  843.          (time_t)0L,
  844.          0L,
  845.          db);
  846.     word_count++;
  847.   }
  848.  
  849.   /* for debugging
  850.   if(char_count - 1 != strlen(line)) {
  851.     waislog(WLOG_HIGH, WLOG_ERROR, 
  852.         "char_count: %ld, strlen: %ld", char_count, strlen(line));
  853.   }
  854.   */
  855.   if('\n' != line[char_count-2])
  856.     *newline_terminated = false;
  857.   else
  858.     *newline_terminated = true;
  859.  
  860.   *line_length = char_count - 1;
  861.   return(word_count);
  862. }
  863.  
  864. static int nodecompare _AP((unsigned long* i,unsigned long* j));
  865.  
  866. static int
  867. nodecompare(i,j)
  868. unsigned long *i, *j;
  869. {
  870.   if (i[0] < j[0])
  871.     return(-1);
  872.   else if (i[0] > j[0])
  873.     return(1);
  874.   else
  875.     return(0);
  876. }
  877.  
  878. #define nodeRange 256 /* 2048 sprint nodes on a full sized machine - should
  879.                  be passed in */
  880. #define iterations_to_reorder 50 /* 1 is best but slow */
  881.  
  882. static void finish_document
  883. #ifndef SOUND 
  884.   _AP((boolean recountHeader, char* header,char* line,long document_id,
  885.        document_table_entry* the_document_table_entry,
  886.        long file_position_before_line,
  887.        long file_position_before_document,database* db,
  888.        boolean word_position, boolean word_pairs,
  889.        int minwordlen));
  890. #else
  891.   _AP((boolean recountHeader, char* header,char* line,long document_id,
  892.        document_table_entry* the_document_table_entry,
  893.        long file_position_before_line,
  894.        long file_position_before_document,database* db,
  895.        boolean word_position, boolean word_pairs,
  896.        int minwordlen, 
  897.        char* type));
  898. #endif
  899.  
  900. static void
  901. finish_document(recountHeader, header,line,document_id,the_document_table_entry,
  902.         file_position_before_line, file_position_before_document,
  903.         db, word_position, word_pairs,
  904. #ifndef SOUND
  905.         minwordlen)
  906. #else
  907.                 minwordlen, type)
  908. #endif
  909. boolean recountHeader;
  910. char* header;
  911. char* line;
  912. long document_id;
  913. document_table_entry* the_document_table_entry;
  914. long file_position_before_line;
  915. long file_position_before_document;
  916. database* db;
  917. boolean word_position, word_pairs;
  918. #ifdef SOUND
  919. char* type;
  920. #endif
  921. { long line_length;
  922.   boolean newline_terminated;
  923.   long number_of_words = 0;     /* initialize, to make shure (up) */
  924.  
  925.   /* It is very dangerous to index headlines, because the healines could
  926.    * contain words which the document do not contain. It means if one make
  927.    * a literal search the coredump is procuced, because the text positions of
  928.    * these words are not correct.
  929.    */
  930. #ifdef FIELDS /* tung, 3/94 */
  931.   if(db->number_of_fields == 0) {
  932. #endif
  933.     if(0 != strlen(header) && recountHeader){
  934.       /* add weights for the header (if there was one) */
  935.       number_of_words =    /* new variable number_of_words ?? (up) */
  936.         map_over_words(header, document_id, 
  937.                        extra_weight_for_header, 
  938.                        file_position_before_line-
  939.                        file_position_before_document,
  940.                        &line_length, 
  941.                        &newline_terminated,
  942.                        db,
  943.                        add_word_before_pairs,
  944. #ifdef SOUND
  945.                        word_position, word_pairs, minwordlen, type);
  946. #else
  947.                          word_position, word_pairs, minwordlen);
  948. #endif
  949.     }
  950.     if(number_of_words == -1)
  951.       waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  952.       db->total_word_count += number_of_words;
  953.     the_document_table_entry->document_length += number_of_words;
  954. #ifdef FIELDS /* tung, 3/94 */
  955.   }
  956. #endif
  957.   
  958.   if(keyword_filename != NULL){
  959.     /* add keywords from keyword file (if specified on command line) */
  960.     
  961.     char *tmpFileName = NULL;
  962.     FILE* keyword_stream = NULL;
  963.     char line[MAX_LINE_LENGTH];
  964.     
  965.     if(keyword_filename != NULL &&
  966.        strlen(keyword_filename) > 1 &&
  967.        !strcmp(keyword_filename+(strlen(keyword_filename)-2), ".Z"))
  968.       /* it's a .Z file.  First, remove the suffix or many things get confused. */
  969.       keyword_filename[(strlen(keyword_filename)-2)] = 0;
  970.     
  971.     if(probe_file(keyword_filename)) {
  972.       keyword_stream = s_fopen(keyword_filename, "r");
  973.     }
  974.     else if(probe_file_possibly_compressed(keyword_filename)) {
  975.       tmpFileName = s_fzcat(keyword_filename);
  976.       if (tmpFileName) {
  977.         keyword_stream = s_fopen(keyword_filename, "r");
  978.         unlink(tmpFileName);
  979.         free(tmpFileName);
  980.       }
  981.     }
  982.     
  983.     if(NULL == keyword_stream)
  984.       waislog(WLOG_HIGH, WLOG_ERROR, 
  985.            "Unable to open keyword file %s", keyword_filename);
  986.     else  { /* read keyword_file, index its contents */
  987.       waislog(WLOG_HIGH, WLOG_INDEX, 
  988.            "Indexing keyword file %s", keyword_filename);
  989.       while(TRUE){
  990.      /* read a line */
  991.      if( !fgets(line, MAX_LINE_LENGTH, keyword_stream) )
  992.        break;  /* eof */
  993.      number_of_words =
  994.        map_over_words(line, document_id, 
  995.               keyword_weight, 
  996.               0,
  997.               &line_length, 
  998.               &newline_terminated,
  999.               db,
  1000.               add_word_before_pairs,
  1001. #ifdef SOUND
  1002.               word_position, word_pairs, minwordlen, type);
  1003. #else
  1004.               word_position, word_pairs, minwordlen);
  1005. #endif
  1006.      if(number_of_words == -1)
  1007.        waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  1008.      db->total_word_count += number_of_words;
  1009.       the_document_table_entry->document_length += number_of_words;
  1010.       }
  1011.       s_fclose(keyword_stream);
  1012.     }
  1013.   }
  1014.   
  1015.   
  1016.   /* store out the document header here */
  1017.   the_document_table_entry->headline_id = 
  1018.     write_headline_table_entry(header, db);
  1019.   if(NULL == line)
  1020.     { /* EOF */
  1021.       /* if it goes to the end of the file, then
  1022.        * set the end_character to 0 so that it is clear that
  1023.        * it goes to the end of the file.
  1024.        */
  1025.       the_document_table_entry->end_character = 0;
  1026.     }
  1027.   else                /* set the end_character */
  1028.     the_document_table_entry->end_character = file_position_before_line;
  1029.   
  1030.   
  1031.   /* waislog("start char: %ld, end char: %ld", 
  1032.    * the_document_table_entry->start_character,
  1033.    * the_document_table_entry->end_character);
  1034.    */
  1035.   
  1036.   if (indexingForBeta)
  1037.     { /* we need to decide which sprint node this doc will go in.
  1038.        * for now we will store the sn in the date field, but that
  1039.        * is temporary
  1040.        * NOTE that we must subract 1 from document_id, since we want
  1041.        * a 0 based number
  1042.        */
  1043.       static unsigned long* nodes = NULL; /* size/node# inited to 0 to 2047 */
  1044.       static long minPos;
  1045.       unsigned long size;
  1046.       
  1047.       if (nodes == NULL)
  1048.     { long i;
  1049.       long startPos;
  1050.       time_t temp_time;
  1051.       
  1052.       nodes = (unsigned long*)s_malloc(sizeof(unsigned long)*nodeRange*2);
  1053.       srand((int)time(&temp_time)); /* try to distribute the entries */
  1054.       startPos = rand() % nodeRange; /* for indexes with < nodeRng docs */
  1055.       for (i = 0; i < nodeRange; i++)
  1056.         { nodes[(i * 2) + 1] = (i + startPos) % nodeRange; 
  1057.           nodes[i * 2] = 0;
  1058.         }
  1059.       minPos = 0;
  1060.       /*printf("init: ");
  1061.         for (i = 0; i < nodeRange; i++)
  1062.         printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  1063.         NL();*/
  1064.     }
  1065.       
  1066.       /* place the document in the emptiest node (at minPos) */
  1067.       the_document_table_entry->date = (time_t)nodes[(minPos * 2) + 1];
  1068.       
  1069.       /* increment the size to account for document */
  1070.       size = nodes[minPos * 2];
  1071.       size += (the_document_table_entry->end_character - 
  1072.            the_document_table_entry->start_character);
  1073.       nodes[minPos * 2] = size;
  1074.       
  1075.       if ((the_document_table_entry->end_character - 
  1076.        the_document_table_entry->start_character) > 100000)
  1077.     printf("big doc %lu %s\n",the_document_table_entry->end_character - the_document_table_entry->start_character,header);
  1078.       
  1079.       minPos++;
  1080.       
  1081.       /* possibly reorder it */
  1082.       if (minPos > iterations_to_reorder)
  1083.     { 
  1084.       long i;
  1085.       minPos = 0;
  1086.       /*printf("before: ");
  1087.         for (i = 0; i < nodeRange; i++)
  1088.         printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  1089.         NL();*/
  1090.       qsort((char*)nodes,nodeRange,sizeof(unsigned long) * 2,nodecompare);
  1091.       /*printf("after: ");
  1092.         for (i = 0; i < nodeRange; i++)
  1093.         printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  1094.         NL();*/
  1095.       printf("just sorted nodes, min: ");
  1096.       for (i = 0; i < 10; i++)
  1097.         printf("%lu ",nodes[i * 2]);
  1098.       printf(", max: %lu/%lu\n",nodes[(nodeRange * 2)-2],nodes[(nodeRange * 2)-1]); 
  1099.     }
  1100.       
  1101.       
  1102.       
  1103. #ifdef old
  1104.       sn = (document_id - 1) % 2048; /* 2048 = sn's in a full machine */
  1105.       
  1106.       /* should also take into account the "fullness" of any particular
  1107.      node */
  1108.       the_document_table_entry->date = (time_t)sn;
  1109.       /* waislog(WLOG_LOW, WLOG_INFO, 
  1110.      "put %s in sprint node %ld",header,sn);*/
  1111. #endif /* def old */
  1112.     }
  1113.   
  1114.   write_document_table_entry(the_document_table_entry, db);
  1115.   cprintf(PRINT_AS_INDEXING, ".");
  1116.   total_indexed_file_length =    /* set this so the speed looks right */
  1117.     total_indexed_file_length + file_position_before_line;
  1118.   total_indexed_file_length =    /* set it back */
  1119.     total_indexed_file_length - file_position_before_line;
  1120. }
  1121.  
  1122. #define LENGTH_OF_NEWLINE 1 /* this will be 2 on a PC, I think */  
  1123.  
  1124. #ifdef NEW_WEIGHT /* tung, 5/94 */
  1125. long number_of_bucket_ids = 0;     /* used in hash.c, irhash.c */
  1126. #endif
  1127.  
  1128. /* void index_text_file(filename,
  1129.              separator_function,
  1130.              header_function,
  1131.              date_function,
  1132.              finish_header_function, 
  1133.              type,
  1134.              db,
  1135.              check_for_text_file,
  1136.              check_for_file_already_indexed,
  1137.              word_position, word_pairs, minwordlen) */
  1138. #ifdef FIELDS /* tung, 5/94 */
  1139. long index_text_file(filename, dataops, db,
  1140.              check_for_text_file,
  1141.              check_for_file_already_indexed,
  1142.              word_position, word_pairs, field_id)    
  1143. #else
  1144. long index_text_file(filename, dataops, db,
  1145.                 check_for_text_file,
  1146.              check_for_file_already_indexed,
  1147.              word_position, word_pairs)     
  1148. #endif
  1149. char* filename;
  1150. dataopsrec* dataops;
  1151. /*
  1152. boolfunc *separator_function;
  1153. voidfunc *header_function;
  1154. longfunc *date_function;
  1155. voidfunc *finish_header_function;
  1156. char *type;
  1157. */
  1158. database* db;
  1159. boolean check_for_text_file;
  1160. boolean check_for_file_already_indexed;
  1161. boolean word_position, word_pairs;
  1162. #ifdef FIELDS /* tung, 5/94 */
  1163. long field_id;
  1164. #endif
  1165. {
  1166.   /* Addes words to the index for a given file.  
  1167.    * The function arguments can be NULL which means it would 
  1168.    *  always answer NULL.  
  1169.    * separator_function is called on every line to see if it 
  1170.    *  separates documents.  
  1171.    * header_function is called on every line so that a headline 
  1172.    *  can be accumulated.  This assumes that it will side effect global 
  1173.    *  variables.
  1174.    * finish_header_function is called when the document is finished 
  1175.    *  (by separator function responding TRUE or EOF) this will return 
  1176.    *  the headline string or NULL. 
  1177.    *  Presumably finish_header_function will use the
  1178.    *  effects of header_function.  finish_header_function 
  1179.    *  will only be called once, so it should clear whatever state 
  1180.    *  header_function has set.
  1181.    * if check_for_text_file then it looks to see if first character
  1182.    *  in the file is a printable character.
  1183.    * if check_for_file_already_indexed then it looks through the filename 
  1184.    *  file to see if the file has not been indexed.  If it has,
  1185.    *  then it is checked to see if it is up-to-date. (it does not 
  1186.    *  kill the old entry (maybe it should)).
  1187.    */
  1188.  
  1189.   long filename_id;
  1190.   document_table_entry the_document_table_entry;
  1191.   long document_id = next_document_id(db);
  1192.  
  1193. /*  FILE* input_stream = s_fopen(filename, "r"); */
  1194.   FILE* input_stream;
  1195.   char *tmpFileName = NULL;
  1196.  
  1197.   long file_position_before_line = 0;
  1198.   long file_position_before_document = 0;
  1199.   long date;
  1200. #ifdef FIELDS /* tung, 1/94 */
  1201.   long number_of_not_ended_section = 0;
  1202. #endif
  1203.   int  use_url = 0;             /* use URL for filename ? */
  1204.   
  1205.  
  1206.    if(filename != NULL &&
  1207.       strlen(filename) > 1 &&
  1208.       !strcmp(filename+(strlen(filename)-2), ".Z"))
  1209.      /* it's a .Z file.  First, remove the suffix or many things get confused. */
  1210.      filename[(strlen(filename)-2)] = 0;
  1211.      
  1212.    if(filename != NULL &&
  1213.       strlen(filename) > 2 &&
  1214.       !strcmp(filename+(strlen(filename)-3), ".gz"))
  1215.      /* it's a .gz file.  First, remove the suffix or many things get confused. */
  1216.      filename[(strlen(filename)-3)] = 0;
  1217.      
  1218. /* multitype extensions */
  1219. /* 
  1220.    If dataops->multitype (primary and secondary type) is defined, then
  1221.    we need to index filenames that have an extension with the
  1222.    dataops->type (primary type) and skip all other files.
  1223.    
  1224.    The only problem with this approach is that we may loose files which 
  1225.    dont have an instance of the primary file type 
  1226. */
  1227.    if ( (dataops->multitype != NULL) && 
  1228.        strcmp(filename+(strlen(filename)-strlen(dataops->type)), dataops->type)) {
  1229.      waislog(WLOG_HIGH, WLOG_INFO, "Skipping file: %s",
  1230.          filename);
  1231.        return(-1);
  1232.    }
  1233.  
  1234.  
  1235.  
  1236.   if(probe_file(filename)) {
  1237.     input_stream = s_fopen(filename, "r");
  1238.   } else {
  1239.     if(probe_file_possibly_compressed(filename)) {
  1240.       tmpFileName = s_fzcat(filename);
  1241.       if (tmpFileName) {
  1242.         input_stream = s_fopen(tmpFileName, "r");
  1243.         unlink(tmpFileName);
  1244.         free(tmpFileName);
  1245.       }
  1246.     } else {
  1247.       waislog(WLOG_HIGH, WLOG_INDEX, 
  1248.               "File %s not readable", filename);
  1249.       return(0);              /* silently skip it */
  1250.     }
  1251.   }
  1252. /* end of this long one */
  1253.  
  1254.  
  1255.  
  1256.   if(NULL == input_stream){
  1257.     waislog(WLOG_HIGH, WLOG_ERROR, 
  1258.         "File %s does not exist", filename);
  1259.     /* then the is not a valid file to be indexed */
  1260.     return(0);
  1261.   }
  1262.   if(check_for_file_already_indexed){
  1263.     time_t time;
  1264.     char full_path[MAX_FILENAME_LEN];
  1265.     truename(filename, full_path);
  1266.     if(true == filename_in_database(full_path, dataops->type, &time, db)){
  1267.       /* check that it is the same time as this file */
  1268.       if(time == file_write_date(filename)){
  1269.     waislog(WLOG_HIGH, WLOG_INDEX, 
  1270.         "File %s already indexed", filename);
  1271.     s_fclose(input_stream);
  1272.     return(0);              /* silently skip it */
  1273.       }
  1274.     }
  1275.   }
  1276.     
  1277.   /* Make the current filename accessible via global variables.
  1278.    * Increment current_filecount so routines can efficiently detect
  1279.    * changes in the current file.
  1280.    * -- Prentiss Riddle, Rice ONCS, riddle@rice.edu, 5/6/92
  1281.    */
  1282.  
  1283.   if(current_filename == NULL) current_filename = s_malloc(MAX_FILENAME_LEN+1);
  1284.  
  1285.   if (URL_prefix && !strncmp(filename, URL_trim, MINIMUM(strlen(URL_trim), strlen(filename)))) {
  1286.     /* trim capable */
  1287.     strcpy(current_filename, URL_prefix);
  1288.     strcat(current_filename, filename+strlen(URL_trim));
  1289.     use_url = 1;
  1290.   } else {
  1291.     strncpy(current_filename, filename, MAX_FILENAME_LEN);
  1292.   }
  1293.   current_filecount++;
  1294.  
  1295.   if(check_for_text_file){ 
  1296.     /* if we need this to be a text file, check the first character
  1297.        for a printable character */
  1298.     long ch = fgetc(input_stream);
  1299.     /* printf("First character is '%c'\n", ch); */
  1300.     if(EOF == ch || (!isprint(ch) && !isspace(ch))){
  1301.       s_fclose(input_stream);
  1302.       return(0);
  1303.     }
  1304.     ungetc(ch, input_stream);
  1305.   }
  1306.   
  1307.  
  1308. /* multitype extensions */
  1309.  
  1310.   /* write out the filename */
  1311.   if ( dataops->multitype != NULL ) {
  1312. #ifdef URLDOCID
  1313.     if (use_url) {              /* make the URL the filename */
  1314.       filename_id = 
  1315.         write_filename_table_entry(current_filename, dataops->multitype, db);
  1316.     } else {
  1317. #endif
  1318.       filename_id =
  1319.         write_filename_table_entry(filename, dataops->multitype, db);
  1320. #ifdef URLDOCID
  1321.     }
  1322. #endif
  1323.   } else {
  1324. #ifdef URLDOCID
  1325.     if (use_url){             /* make the URL the filename  */
  1326.       filename_id = 
  1327.         write_filename_table_entry(current_filename, dataops->type, db);
  1328.     } else {
  1329. #endif
  1330.       filename_id = 
  1331.         write_filename_table_entry(filename, dataops->type, db);
  1332. #ifdef URLDOCID
  1333.     }
  1334. #endif
  1335.   }
  1336.   
  1337.   
  1338.   /*  (if (not *drop_table*) (make_drop_table)) maybe put in later */
  1339.   
  1340.   header_flag_1 = NULL;
  1341.   the_document_table_entry.filename_id = filename_id;
  1342.   the_document_table_entry.start_character = 0;
  1343.   the_document_table_entry.document_length = 0;
  1344.   the_document_table_entry.number_of_lines = 0;
  1345.   the_document_table_entry.date = 0;
  1346.  
  1347. #ifdef NEW_WEIGHT /* tung, 5/94 */
  1348.   number_of_bucket_ids = 0;
  1349. #endif
  1350.  
  1351.   while(TRUE){
  1352.     long line_length;
  1353.     boolean newline_terminated;
  1354.     char line[MAX_LINE_LENGTH];
  1355.     char header[MAX_LINE_LENGTH];
  1356.     char* read_line_result;
  1357.     boolean eof;
  1358. #ifdef FIELDS /* tung, 1/94 */
  1359.     long current_id; 
  1360. #endif
  1361.  
  1362.     /* printf("ftell: %ld\n", ftell(input_stream)); */
  1363.     /* read a line */
  1364.     read_line_result  = fgets(line, MAX_LINE_LENGTH, input_stream);
  1365.     beFriendly();
  1366.  
  1367.     /* eof = feof(input_stream); */ /* zero means not eof */
  1368.     eof    = !read_line_result; 
  1369.     
  1370.     the_document_table_entry.number_of_lines++;
  1371.  
  1372.     header[0] = '\0';        /* set it to the empty string */
  1373.  
  1374.     if(eof ||
  1375.        ((NULL != dataops->separator_function) && dataops->separator_function(line)) || (keyword_filename != NULL) ){
  1376.  
  1377.     /* tell this function that there is not more to process */
  1378.       if (keyword_filename != NULL) {
  1379.         eof = true; 
  1380.       } 
  1381.   
  1382.  
  1383.       /* we are processing a separator, therefore we should
  1384.        * finish off the last document, and start a new one
  1385.        */
  1386.       if(NULL != dataops->finish_header_function){
  1387.     dataops->finish_header_function(header);
  1388.  
  1389. /* call Victor Nettoyage :-( */
  1390.         (void)cleanHeadline(header);
  1391.         
  1392.       }
  1393.       if(0 == strlen(header)){
  1394.     char full_path[1000];
  1395.     char directory[1000];
  1396.     if (!URL_prefix) {
  1397.       truename(filename, full_path);
  1398.       sprintf(header, "%s   %s", pathname_name(full_path),
  1399.         pathname_directory(full_path, directory));
  1400.     } else
  1401.           strncpy(header, current_filename, MAX_FILENAME_LEN);
  1402.       }
  1403.       the_document_table_entry.number_of_lines--; /* dont count separator */
  1404.       /* finish off the last */
  1405.  
  1406.       finish_document( dataops->extraheaderweight,
  1407.               header, line, document_id,
  1408.               &the_document_table_entry,
  1409.               eof?    /* if EOF, use file length */
  1410.               file_length(input_stream):file_position_before_line, 
  1411.               file_position_before_document,
  1412.               db, word_position, word_pairs,
  1413. #ifndef SOUND
  1414.               dataops->minwordlen);
  1415. #else
  1416.               dataops->minwordlen, dataops->indextype);
  1417. #endif
  1418.       /* initialize the next one */
  1419.       the_document_table_entry.filename_id = filename_id;
  1420.       the_document_table_entry.start_character = file_position_before_line;
  1421.       the_document_table_entry.number_of_lines = 1; /* count separator */
  1422.       the_document_table_entry.date = 0;
  1423.       the_document_table_entry.document_length = 0;
  1424.       file_position_before_document = file_position_before_line;
  1425.  
  1426.       document_id = next_document_id(db);    
  1427.  
  1428.       if(!eof)
  1429.     {            /* not EOF */
  1430.          if(NULL != dataops->header_function){
  1431.             dataops->header_function(line);
  1432.           }
  1433.      if (dataops->date_function != NULL &&
  1434.               (date = dataops->date_function(line)) > 0)
  1435.             the_document_table_entry.date = date;
  1436.      /* dgg -- don't know where this goes. */
  1437.  
  1438.          if (dataops->addseparatorwords) { /* dgg */
  1439.            long number_of_words;
  1440.            number_of_words = map_over_words(line, document_id, dataops->repeat_weight,
  1441.                                             file_position_before_line -
  1442.                                             file_position_before_document,
  1443.                                             &line_length,
  1444.                                             &newline_terminated,
  1445.                                             db,
  1446.                                             add_word_before_pairs,
  1447.                                             word_position, word_pairs,
  1448. #ifdef SOUND
  1449.                                             dataops->minwordlen,
  1450.                                             dataops->indextype);
  1451. #else
  1452.                                             dataops->minwordlen);
  1453. #endif
  1454.           the_document_table_entry.document_length += number_of_words;
  1455.           len_of_files_since_last_delete += number_of_words;
  1456.           len_of_files_since_last_flush += number_of_words;
  1457.     }
  1458.     else {
  1459.       line_length = strlen(line);
  1460.       newline_terminated = true;
  1461.     }
  1462.       }
  1463. #ifdef NEW_WEIGHT /* tung, 5/94 */
  1464.       assign_term_weight_for_doc(&number_of_bucket_ids, db);
  1465.       if(eof) {
  1466.     s_fclose(input_stream);
  1467.     return(1);
  1468.       }
  1469. #else
  1470.       else{            /* EOF */
  1471.     /* printf("closing the file\n"); */
  1472.     s_fclose(input_stream);
  1473.     return(1);
  1474.       }
  1475. #endif
  1476. #ifdef FIELDS /* tung, 10/94 */
  1477.   number_of_not_ended_section = 0;
  1478. #endif
  1479.     }
  1480.         
  1481.     else{               
  1482.       /* not a separator or EOF so process the line */
  1483.       long number_of_words;
  1484.       if (dataops->date_function != NULL && 
  1485.       the_document_table_entry.date == 0 &&
  1486.            (date = dataops->date_function(line)) > 0) 
  1487.     the_document_table_entry.date = date;
  1488.  
  1489.       if(NULL != dataops->header_function) dataops->header_function(line);
  1490.  
  1491.       if(index_contents ) {
  1492.         if( _indexable_section) {
  1493. #ifdef FIELDS /* tung, 1/94 */
  1494.           if((field_id >= 0) && (db->number_of_fields > 0)) {
  1495.             current_id = how_index_line(field_id, line, 
  1496.                                         &number_of_not_ended_section,
  1497.                                         document_id, 
  1498.                                         dataops->repeat_weight, 
  1499.                                         file_position_before_line -
  1500.                                         file_position_before_document,
  1501.                                         &line_length, 
  1502.                                         &newline_terminated,
  1503.                                         db,    
  1504.                                         add_word_before_pairs,
  1505.                                         field_add_word_before_pairs,
  1506.                                         word_position, word_pairs,
  1507.                                         dataops->minwordlen,
  1508.                                         dataops->indextype);
  1509.             
  1510.             number_of_words = count_words(line, &line_length, 
  1511.                                           &newline_terminated);
  1512.           }
  1513.           else {
  1514.             dataops->indextype = dataops->prev_indextype;
  1515. #endif /* #ifdef FIELDS */
  1516.             number_of_words = map_over_words(line, document_id, dataops->repeat_weight, 
  1517.                                              file_position_before_line -
  1518.                                              file_position_before_document,
  1519.                                              &line_length, 
  1520.                                              &newline_terminated,
  1521.                                              db,    
  1522.                                              add_word_before_pairs,
  1523.                                              word_position, word_pairs,
  1524. #ifdef SOUND
  1525.                                              dataops->minwordlen,
  1526.                                              dataops->indextype);
  1527. #else
  1528.             dataops->minwordlen);
  1529. #endif
  1530. #ifdef FIELDS /* tung, 1/94 */
  1531.         } /* else */
  1532. #endif
  1533.       if(number_of_words == -1)
  1534.         waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  1535.       the_document_table_entry.document_length += number_of_words;
  1536.       len_of_files_since_last_delete += number_of_words;
  1537.       len_of_files_since_last_flush += number_of_words;
  1538.           db->total_word_count += number_of_words;
  1539.         }
  1540.         else
  1541.           newline_terminated = 0;
  1542.       }
  1543.     }
  1544.     if(newline_terminated)
  1545.       file_position_before_line += (line_length + 
  1546.                     LENGTH_OF_NEWLINE /* in case of crlf */
  1547.                     - 1 /* fgets gets one newline */
  1548.                     );
  1549.     else
  1550.       file_position_before_line = ftell(input_stream);
  1551.  
  1552.     
  1553.     /* for debugging
  1554.     if(file_position_before_line != ftell(input_stream)) {
  1555.       waislog(WLOG_LOW, WLOG_INFO, "ftell: %ld, computed ftell: %ld", 
  1556.          ftell(input_stream),
  1557.          file_position_before_line);
  1558.          }
  1559.          */    
  1560.  
  1561.   }
  1562.   printf("test\n");
  1563. }
  1564. /* --------------------------------------------------------------- */
  1565. #ifdef FIELDS /* tung, 1/94 */
  1566. long field_index_text_file(filename, dataops, db,
  1567.                check_for_text_file,
  1568.                check_for_file_already_indexed,
  1569.                word_position, word_pairs, field_id)     
  1570.      char* filename;
  1571.      dataopsrec* dataops;
  1572.      database* db;
  1573.      boolean check_for_text_file;
  1574.      boolean check_for_file_already_indexed;
  1575.      boolean word_position, word_pairs;
  1576.      long field_id;
  1577. {
  1578.   long document_id = next_document_id(db);
  1579.   long file_position_before_line = 0;
  1580.   long file_position_before_document = 0;
  1581.   long number_of_not_ended_section = 0;
  1582.   long i;
  1583.   FILE* input_stream = NULL;
  1584.   char *tmpFileName = NULL;
  1585.   char file[256];
  1586.  
  1587.   if(filename != NULL &&
  1588.      strlen(filename) > 1 &&
  1589.      !strcmp(filename+(strlen(filename)-2), ".Z"))
  1590.     /* 
  1591.      * it's a .Z file.  First, remove the suffix or many things get confused. 
  1592.      */
  1593.     filename[(strlen(filename)-2)] = 0;
  1594.   if(probe_file(filename)) {
  1595.     input_stream = s_fopen(filename, "r");
  1596.   }
  1597.   else if(probe_file_possibly_compressed(filename)) {
  1598.     tmpFileName = s_fzcat(filename);
  1599.     if (tmpFileName) {
  1600.       input_stream = s_fopen(tmpFileName, "r");
  1601.       unlink(tmpFileName);
  1602.       free(tmpFileName);
  1603.     }
  1604.   }
  1605.   
  1606.   /* Make the current filename accessible via global variables.
  1607.    * Increment current_filecount so routines can efficiently detect
  1608.    * changes in the current file.
  1609.    * -- Prentiss Riddle, Rice ONCS, riddle@rice.edu, 5/6/92
  1610.    */
  1611.   
  1612.   if(current_filename == NULL) 
  1613.     current_filename = s_malloc(MAX_FILENAME_LEN+1);
  1614.   
  1615.   if (URL_prefix && !strncmp(filename, URL_trim, MINIMUM(strlen(URL_trim), strlen(filename)))) {
  1616.     /* trim capable */
  1617.     strcpy(current_filename, URL_prefix);
  1618.     strcat(current_filename, filename+strlen(URL_trim));
  1619.   } else
  1620.     strncpy(current_filename, filename, MAX_FILENAME_LEN);
  1621.   
  1622.   if(check_for_text_file){ 
  1623.     /* if we need this to be a text file, check the first character
  1624.        for a printable character */
  1625.     long ch = fgetc(input_stream);
  1626.     /* printf("First character is '%c'\n", ch); */
  1627.     if(EOF == ch || (!isprint(ch) && !isspace(ch))){
  1628.       s_fclose(input_stream);
  1629.       return(0);
  1630.     }
  1631.     ungetc(ch, input_stream);
  1632.   }
  1633.   if(NULL == input_stream){
  1634.     waislog(WLOG_HIGH, WLOG_ERROR, 
  1635.         "File %s does not exist", filename);
  1636.     /* then the is not a valid file to be indexed */
  1637.     return(0);
  1638.   }
  1639.  
  1640. #ifdef NEW_WEIGHT /* tung, 5/94 */
  1641.   number_of_bucket_ids = 0;
  1642. #endif
  1643.  
  1644.   while(TRUE){
  1645.     long line_length;
  1646.     boolean newline_terminated;
  1647.     char line[MAX_LINE_LENGTH];
  1648.     char header[MAX_LINE_LENGTH];
  1649.     char* read_line_result;
  1650.     boolean eof;
  1651.     long current_id; 
  1652.  
  1653.     /* printf("ftell: %ld\n", ftell(input_stream)); */
  1654.     /* read a line */
  1655.     read_line_result  = fgets(line, MAX_LINE_LENGTH, input_stream);
  1656.     beFriendly();
  1657.     
  1658.     eof    = !read_line_result; 
  1659.     
  1660.     if(eof || 
  1661.        ((NULL != dataops->separator_function) && dataops->separator_function(line)) || (keyword_filename != NULL)) {
  1662.       
  1663.       file_position_before_document = file_position_before_line;
  1664.       
  1665.       document_id = ++(db->doc_table_allocated_entries);
  1666.       
  1667.       if(!eof)
  1668.     {            /* not EOF */
  1669.           if (dataops->addseparatorwords) { /* dgg */
  1670.             long number_of_words;
  1671.             number_of_words = map_over_words(line, document_id, 
  1672.                                              dataops->repeat_weight,
  1673.                                              file_position_before_line -
  1674.                                              file_position_before_document,
  1675.                                              &line_length,
  1676.                                              &newline_terminated,
  1677.                                              db,
  1678.                                              add_word_before_pairs,
  1679.                                              word_position, word_pairs,
  1680. #ifdef SOUND
  1681.                                              dataops->minwordlen,
  1682.                                              dataops->indextype);
  1683. #else
  1684.             dataops->minwordlen);
  1685. #endif
  1686.  
  1687.           }
  1688.           else {
  1689.             line_length = strlen(line);
  1690.             newline_terminated = true;
  1691.           }
  1692.         }
  1693. #ifdef NEW_WEIGHT /* tung, 5/94 */
  1694.       assign_term_weight_for_doc(&number_of_bucket_ids, db);
  1695.       if(eof) {
  1696.     s_fclose(input_stream);
  1697.     return(1);
  1698.       }
  1699. #else
  1700.       else{            /* EOF */
  1701.     /* printf("closing the file\n"); */
  1702.     s_fclose(input_stream);
  1703.     return(1);
  1704.       }
  1705. #endif
  1706. #ifdef FIELDS /* tung, 10/94 */
  1707.     number_of_not_ended_section = 0;
  1708. #endif
  1709.     }
  1710.   
  1711.     else{               
  1712.       /* not a separator or EOF so process the line */
  1713.       long number_of_words;
  1714.       
  1715.       if(index_contents ) {
  1716.     if( _indexable_section) {
  1717.           
  1718.           current_id = how_index_line(field_id, line, 
  1719.                                       &number_of_not_ended_section,
  1720.                                       document_id, 
  1721.                                       dataops->repeat_weight, 
  1722.                                       file_position_before_line -
  1723.                                       file_position_before_document,
  1724.                                       &line_length, 
  1725.                                       &newline_terminated,
  1726.                                       db,    
  1727.                                       add_word_before_pairs,
  1728.                                       field_add_word_before_pairs,
  1729.                                       word_position, word_pairs,
  1730.                                       dataops->minwordlen,
  1731.                                       dataops->indextype);
  1732.           
  1733.           number_of_words = count_words(line, &line_length, 
  1734.                                         &newline_terminated);
  1735.       if(number_of_words == -1)
  1736.         waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  1737.         }
  1738.         else
  1739.           newline_terminated = 0;
  1740.       }
  1741.       if(newline_terminated)
  1742.         file_position_before_line += (line_length + 
  1743.                                       LENGTH_OF_NEWLINE /* in case of crlf */
  1744.                                       - 1 /* fgets gets one newline */
  1745.                                       );
  1746.       else
  1747.         file_position_before_line = ftell(input_stream);
  1748.     }
  1749.   }
  1750. }
  1751. #endif
  1752.  
  1753. /* ---------------------------------------------------------------- */
  1754.  
  1755. /* return TRUE if it is a directory, FALSE otherwise */
  1756. boolean directoryp(file)
  1757. char *file;
  1758.  
  1759. {
  1760. #ifdef THINK_C
  1761.   return(false);
  1762. #else
  1763.   struct stat stbuf;
  1764.   if(stat(file, &stbuf) == -1)
  1765.     return(FALSE);
  1766.   if((stbuf.st_mode & S_IFMT) == S_IFDIR)
  1767.     return(true);
  1768.   return(FALSE);
  1769. #endif
  1770. }
  1771.  
  1772. /* return true if it is a file, FALSE otherwise */
  1773. boolean filep(file)
  1774. char *file;
  1775. {
  1776. #ifdef THINK_C
  1777.  return(probe_file(file));
  1778. #else
  1779.   struct stat stbuf;
  1780.   if(stat(file, &stbuf) == -1)
  1781.     return(FALSE);
  1782.   if(!((stbuf.st_mode & S_IFMT) == S_IFDIR))
  1783.     return(true);
  1784.   return(FALSE);
  1785. #endif
  1786. }
  1787.  
  1788. /* recursively indexes the directory specified. 
  1789.  * If it is a file, then index it. 
  1790.  */
  1791. void index_directory(file, dataops,  db,
  1792.              check_for_text_file,
  1793.              check_for_file_already_indexed,
  1794.              word_position, word_pairs
  1795. #ifdef FIELDS /* tung, 5/94 */
  1796.              ,index_next_field, field_id
  1797. #endif
  1798.              )
  1799. char *file;
  1800. dataopsrec*  dataops;
  1801. database* db;
  1802. boolean check_for_text_file;
  1803. boolean check_for_file_already_indexed;
  1804. boolean word_position, word_pairs;
  1805. #ifdef FIELDS /* tung, 5/94 */
  1806. boolean index_next_field;
  1807. long field_id;
  1808. #endif
  1809. {
  1810. #ifndef THINK_C
  1811.   struct dirent **list;
  1812.   long i, j;
  1813. #ifdef FIELDS /* tung, 5/94 */
  1814.   long mesg;
  1815. #endif
  1816.   if(filep(file)){
  1817. #ifndef FIELDS /* 5/94, tung */
  1818.     waislog(WLOG_MEDIUM, WLOG_INDEX,
  1819.         "Indexing file: %s",  file);
  1820. #else
  1821.     waislog(WLOG_MEDIUM, WLOG_INDEX, "File: %s",  file);
  1822. #endif
  1823. #ifdef FIELDS /* 1/94, tung */
  1824.     if(index_next_field && db->number_of_fields) {
  1825.       if((mesg = field_index_text_file(file, dataops, db, 
  1826.                        check_for_text_file, 
  1827.                        check_for_file_already_indexed,
  1828.                        word_position, word_pairs, field_id)) <= 0) {
  1829.     waislog(WLOG_HIGH,WLOG_ERROR,"File not indexed: %s",file);
  1830.     if(mesg = -1) {
  1831.       closeDatabase(db);
  1832.       exit(0);
  1833.     }
  1834.       }
  1835.     }
  1836.     else {
  1837.       if((mesg = index_text_file(file, dataops, db,
  1838.                  check_for_text_file,
  1839.                  check_for_file_already_indexed,
  1840.                  word_position, word_pairs, field_id)) <= 0) {
  1841.     waislog(WLOG_HIGH,WLOG_ERROR,"File not indexed: %s",file);
  1842.     if(mesg = -1) {
  1843.       closeDatabase(db);
  1844.       exit(0);
  1845.     }
  1846.       }
  1847.     }
  1848. #else
  1849.     if(index_text_file(file, dataops, db,
  1850.                check_for_text_file,
  1851.                check_for_file_already_indexed,
  1852.                word_position, word_pairs) <= 0)
  1853.       waislog(WLOG_HIGH,WLOG_ERROR,"File not indexed: %s",file);
  1854. #endif
  1855.   }
  1856.   else if(directoryp(file)){
  1857.     if ((i = scandir(file, &list, NULL, NULL)) < 0) {
  1858.       return;
  1859.     }
  1860.     for(j = 0; j < i; j++) {
  1861.       char name[1000];        /* max filename size */
  1862.       
  1863.       if(strcmp(list[j]->d_name, ".") == 0
  1864.      || strcmp(list[j]->d_name, "..") == 0
  1865.      )
  1866.     continue;
  1867.       
  1868.       strcpy(name, file);    /* copy the filename into the name variable */
  1869.       strcat(name, "/");
  1870.       strcat(name, list[j]->d_name);
  1871.       index_directory(name, dataops, db,
  1872.               check_for_text_file,
  1873.               check_for_file_already_indexed, 
  1874.               word_position, word_pairs
  1875. #ifdef FIELDS /* 5/94, tung */
  1876.               ,index_next_field, field_id
  1877. #endif
  1878.               );
  1879.     }
  1880.     if(list != NULL) {
  1881.       for (j = 0; j < i; j++)
  1882.     if(list[j] != NULL) free((char *)list[j]);
  1883.       free((char *)list);
  1884.     }
  1885. #endif                /*ndef THINK_C */
  1886.   }
  1887. }
  1888.  
  1889.  
  1890. /* returns a pointer to a string with good stuff */
  1891. char *cleanHeadline (headline)
  1892. char *headline;
  1893. {
  1894.   long length = strlen(headline) + 1; /* include the trailing null */
  1895.   long i,j;
  1896.   Boolean spaceFlag = false;
  1897.  
  1898.  
  1899. #ifdef do_delete_leading_spaces
  1900.   /* delete leading spaces */
  1901.   for(i = 0L; i < strlen(headline); i++){
  1902. /*    if(isprint(headline[i])){ */
  1903.     if(isgraph(headline[i])){
  1904.       break;
  1905.     }
  1906.   }
  1907.   /* and move it */
  1908.   memcpy(headline, headline+i, length);
  1909.  
  1910.   /* 
  1911.   ** - replace all the \n and \r with a space, avoid putting 
  1912.   **   two spaces one after the other
  1913.   */
  1914.   headline = headline + i;
  1915. #endif
  1916.  
  1917.   /* replace carriage returns and line feeds */
  1918.   for (i = 0L, j = 0L; i < strlen(headline)+1; i++) {
  1919.     if ((headline[i] != '\r') && (headline[i] != '\n')) {
  1920.       headline[j++] = headline[i];
  1921.       spaceFlag = false;
  1922.     }
  1923.     else {
  1924.     
  1925.         if ( spaceFlag == true ) {
  1926.            j++;
  1927.         }
  1928.         else {
  1929.           headline[j++] = ' ';
  1930.         }
  1931.         
  1932.         spaceFlag = true;
  1933.     }
  1934.   }
  1935.   
  1936.  
  1937.   /* delete trailing stuff */
  1938.   for(i = strlen(headline) - 1L ; i > 0; i--){
  1939. /*    if(isprint(headline[i])){ */
  1940.     if(isgraph(headline[i])){
  1941.       break;
  1942.     }
  1943.     headline[i] = '\0';
  1944.   }
  1945.   
  1946.   return(headline);
  1947. }
  1948.  
  1949.  
  1950.